"""
Synchronous Job Manager
"""
from rpc import *
from debugtools import *
from gputools import *
import cPickle, multiprocessing, threading, sys, os, os.path, time, hashlib, random, gc

class SyncJobHandler(object):
    def __init__(self):
        self.port = DEFAULT_PORT
        self.ready = False
    
    def set_machine_id(self, machine_id):
        if type(machine_id) is tuple:
            machine_id, port = machine_id
            if port is None:
                port = DEFAULT_PORT
            self.port = port
        self._machine_id = machine_id # name of the current machine
    
    def get_machine_id(self):
        return self._machine_id
    machine_id = property(get_machine_id)
    
#    def set_external_machine(self):
#        """
#        When called by client CLIENT, sets the external machine to be "CLIENT".
#        The automatic calls from other machines to this server will be made on behalf
#        of the external client.
#        """
#        self.external_machine = self.client

    def set_common_handler_id(self, handler_id):
        self.common_handler_id = handler_id
    
    def remote_call(self, machines, fun, *argss, **kwdss):
        """
        Calls 'fun' with '*argss' on 'machines'. 'fun' must be a function of the current handler.
        It is called on the handler associated to the external machine, i.e., the machine
        that originated the synchronous procedure.
        """
        if type(machines) is str or type(machines) is tuple:
            machines = [machines]
        n = len(machines)
        for args in argss:
            if not len(args) == n:
                raise Exception("The arguments must have as many elements as 'machines'")
        # Connects to the machines with the current machine id
        clients = RpcClients(machines,
                             handler_id = self.common_handler_id,
                             handler_class = self.__class__)
        results = getattr(clients, fun)(*argss, **kwdss)

        return results
    
    def set_this_ready(self, value):
        if value:
            log.debug("Machine '%s' is now ready" % self.machine_id)
        self.ready = value
    
    def set_ready(self, machines):
        self.remote_call(machines, 'set_this_ready', [True]*len(machines))
        log.debug("machines '%s' are now ready" % str(machines))
    
    def wait(self):
        """
        Blocks until another machine decides it is ready (thank you multithreading).
        """
        while True:
            if self.ready:
                break
            time.sleep(.1)
        log.debug("Machine '%s' is ready, resuming" % self.machine_id)
        self.set_this_ready(False)
        return True
   
